home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Libraries
/
TurboTCP 1.0.1
/
TurboTCP.source
/
CTCPSessionDoc.cp
< prev
next >
Wrap
Text File
|
1993-12-10
|
22KB
|
989 lines
/*
** CTCPSessionDoc.cp
**
** TurboTCP support library
** TCP session document
**
** Copyright © 1993, FrostByte Design / Eric Scouten
**
*/
#include "CTCPSessionDoc.h"
#ifndef TurboTCPHeaders
#include <pascal.h>
#include <Constants.h>
#include <TBUtilities.h>
#include <CDecorator.h>
#include <CError.h>
#include <CWindow.h>
#endif
#include <CFile.h>
#include "CTCPResolverCall.h"
#include "CTCPStream.h"
#define sessionOpen (cState >= strSYNReceived) && (cState <= strTimeWait)
#define sessionOpening (cState >= strListen) && (cState <= strSYNSent)
#define sessionClosing (cState >= strFINWait1) && (cState <= strTimeWait)
#define ALRT_TCPOpenFailed 23000
#define ALRT_TCPUnexpError 23001
#define ALRT_TCPSendFailed 23002
#define ALRT_TCPTerminated 23003
#define ESTR_TerminBase 23010
#define STR__WindowStrings 23000
#define Wstr_NoSession 1
#define Wstr_Separator 2
#define Wstr_NotReadyPrefix 3
#define Wstr_NotReadySuffix 4
#define Wstr_Untitled 5
#define Wstr_PortDelimiter 6
extern CDecorator *gDecorator;
extern CError *gError;
// —— construction/destruction
/*______________________________________________________________________
**
** ITCPSessionDoc
**
** Initialize the document, including new instance variables.
**
** aSupervisor (CApplication *): the document’s supervisor
** printable (Boolean): TRUE if document can be printed
** rcvBufferSize (long): size of receive buffer to create
** theDefaultPort (b_16): default IP port number
** doUseCName (Boolean): TRUE to get canonical name of remote host
** whenever possible
** autoReceiveSize (short): number of entries in RDS for auto-receive,
** 0 to disable autoreceiving
** autoReceiveNum (short): number of auto-receive calls to issue at once
** must be at least 1
**
*/
void CTCPSessionDoc::ITCPSessionDoc (CApplication *aSupervisor, Boolean printable,
long rcvBufferSize, b_16 theDefaultPort,
Boolean doUseCName, short autoReceiveSize,
short autoReceiveNum)
{
// blank out the usual variables
itsStream = NULL;
itsResolver = NULL;
pendingOpenByName = closeAndQuit = FALSE;
actualPort = defaultPort = theDefaultPort;
hostCName[0] = '\0';
useCName = doUseCName;
sessionReady = FALSE;
goAwayOnClose = FALSE;
showFileName = TRUE;
showHostName = TRUE;
untitledNumber = 0;
CDocument::IDocument(aSupervisor, printable);
// create a TCP stream
TRY {
itsStream = new (CTCPStream);
itsStream->ITCPStream(rcvBufferSize, autoReceiveSize, autoReceiveNum);
itsResolver = new (CTCPResolverCall);
itsResolver->ITCPResolverCall();
DependUpon(itsStream);
DependUpon(itsResolver);
}
CATCH {
ForgetObject(itsStream);
ForgetObject(itsResolver);
}
ENDTRY;
}
/*______________________________________________________________________
**
** Dispose
**
** Get rid of the document’s stream and resolver objects then dispose of self.
**
*/
void CTCPSessionDoc::Dispose (void)
{
// get rid of stream & resolver call objects
if (itsStream) {
itsStream->InstallRcvBypass(NULL, NULL);
itsStream->PostponeDispose();
}
if (itsResolver)
itsResolver->Dispose();
// get rid of the session document object
CDocument::Dispose();
}
// —— opening/closing session
/*______________________________________________________________________
**
** OpenUserHost
**
** Open a connection to the host named by the string. Hostname may be either dotted decimal
** notation or a DNS name. A port number may be specified at the end of the hostname
** (separated by a single space) if the caller permits it.
**
** theHostName (char *): the hostname (and optional port#) string
** theDefaultPort (b_16): the default port number
** allowPortChange (Boolean): TRUE to allow port number overrides
**
*/
void CTCPSessionDoc::OpenUserHost (char *theHostName, b_16 theDefaultPort,
Boolean allowPortChange)
{
Str255 portNumStr;
long newPortNum = 0;
register char *s, *d;
register short i;
// ensure that there is a host to open
if (*theHostName == '\0') {
HandleOpenFailed(nameSyntaxErr);
return;
}
// parse for port number
pendingPortNumber = theDefaultPort;
if (allowPortChange) {
s = theHostName;
d = (char *) &hostCName;
while ((*s != ' ') & (*s != '\0'))
*d++ = *s++;
*d = '\0';
if (*s == ' ') {
d = (char *) &portNumStr;
i = -1;
while (*s != '\0')
*d++ = *s++, i++;
portNumStr[0] = (char) i;
if (i > 0)
StringToNum(portNumStr, &newPortNum);
}
}
else
BlockMove(theHostName, &hostCName, 255);
if (newPortNum != 0)
pendingPortNumber = newPortNum;
// issue resolver command
pendingOpenByName = TRUE;
actualPort = pendingPortNumber;
itsResolver->DoStrToAddr((char *) &hostCName);
}
/*______________________________________________________________________
**
** Close
**
** Close the document. This method also ensures that the TCP stream is gracefully
** closed. If not quitting, delays closure until the MacTCP says the stream has been
** terminated.
**
** quitting (Boolean): TRUE if quitting
**
** return (Boolean): FALSE if close/quit aborted by user
**
*/
Boolean CTCPSessionDoc::Close (Boolean quitting)
{
Boolean hadSessionOpen;
b_16 cState;
// find out if we really want to close
closeAndQuit = quitting;
if (!ConfirmClose(quitting))
return(FALSE);
// yes, we’re really closing; take care of file first
if (itsFile)
itsFile->Close();
// if session is open, give time for session to close
if (itsStream) {
cState = itsStream->ConnectionState();
hadSessionOpen = sessionOpen;
if (sessionOpen)
itsStream->Close();
if (sessionOpening)
itsStream->Abort();
}
else
hadSessionOpen = FALSE;
// close the document – maybe?
if (quitting || !hadSessionOpen || !goAwayOnClose)
return(CDirector::Close(quitting));
else
return(FALSE);
}
// —— document/window naming ——
/*______________________________________________________________________
**
** AutoTitle
**
** Call to automatically set the title for the session document window. Checks the connection
** status and host name and forms an appropriate title. Also updates the “sessionReady”
** field, so a quick test can be used to determine if data can be sent.
**
*/
void CTCPSessionDoc::AutoTitle (void)
{
Str255 fileStr = "\p";
Str255 hostStr = "\p";
Str255 tempStr = "\p";
Boolean wasLocked = this->Lock(TRUE);
// sanity checking…
if (!itsWindow) {
this->Lock(wasLocked);
return;
}
if (!untitledNumber)
untitledNumber = gDecorator->GetWCount();
if (itsStream)
sessionReady = ((hostCName[0] != '\0') && (itsStream->ConnectionState() == strEstablished));
else
sessionReady = FALSE;
// build document name string (if requested)
if (showFileName) {
if (itsFile)
itsFile->GetName(fileStr);
else {
GetIndString(fileStr, STR__WindowStrings, Wstr_Untitled);
NumToString(untitledNumber, tempStr);
ConcatPStrings(fileStr, tempStr);
}
}
// build host name string
if (showHostName) {
if (!hostCName[0])
GetIndString(hostStr, STR__WindowStrings, Wstr_NoSession);
else {
if (!sessionReady) // left “(” if not ready
GetIndString(hostStr, STR__WindowStrings, Wstr_NotReadyPrefix);
BlockMove(&hostCName, &tempStr, 255); // host name, minus trailing “.”
CtoPstr((char *) &tempStr);
if (tempStr[tempStr[0]] == '.')
tempStr[0]--;
ConcatPStrings(hostStr, tempStr);
if (actualPort != defaultPort) { // port number if non-standard
GetIndString(tempStr, STR__WindowStrings, Wstr_PortDelimiter);
ConcatPStrings(hostStr, tempStr);
NumToString(actualPort, tempStr);
ConcatPStrings(hostStr, tempStr);
}
if (!sessionReady) { // right “)” if not ready
GetIndString(tempStr, STR__WindowStrings, Wstr_NotReadySuffix);
ConcatPStrings(hostStr, tempStr);
}
}
if (showFileName) { // “ : ” between file & host name
GetIndString(tempStr, STR__WindowStrings, Wstr_Separator);
ConcatPStrings(fileStr, tempStr);
}
ConcatPStrings(fileStr, hostStr);
}
// set the title
if (showFileName || showHostName) // if neither file nor hostname are requested,
// assume subclass will specify window titles
itsWindow->SetTitle(fileStr);
this->Lock(wasLocked);
}
/*______________________________________________________________________
**
** GetName
**
** Return the name of a document. Overriden to remove the behavior of checking the window
** title. Instead we use the file name, or an assigned “Untitled-nn” title.
**
** theName (Str255): where to return the name
**
*/
void CTCPSessionDoc::GetName (Str255 theName)
{
Str255 tempStr;
if (itsFile)
itsFile->GetName(theName);
else {
GetIndString(theName, STR__WindowStrings, Wstr_Untitled);
NumToString(untitledNumber, tempStr);
ConcatPStrings(theName, tempStr);
}
}
/*______________________________________________________________________
**
** SessionEstablished
**
** Indicates whether a TCP session is established and ready for data.
**
** return (Boolean): TRUEif session is currently established
**
*/
Boolean CTCPSessionDoc::SessionEstablished (void)
{
if (itsStream)
return (itsStream->ConnectionState() == strEstablished);
else
return FALSE;
}
// —— special error handling ——
/*______________________________________________________________________
**
** TCPErrorAlert
**
** Display a customized message to indicate to the user that the connection failed. This
** routine is copied from the TCL’s routine ErrorAlert.
**
** err (OSErr): the error number
** message (long): message code (same as for ErrorAlert, see <TCLUtilities.cp>
** alertID (short): the ALRT/DITL resources to use
** parm3 (short): the number to plug into ^3
**
** return (short): the item which caused the alert to return
**
*/
short CTCPSessionDoc::TCPErrorAlert (OSErr err, long message, short alertID,
short parm3)
{
Str255 errStr;
Str255 hostStr;
Str63 numStr;
Str63 numStr3;
short strIndex, strID;
// silence any further messages
gLastError = kSilentErr;
// see if anyone filled in the message field
errStr[0] = 0;
strIndex = LoShort(message);
if (strIndex > 0) {
strID = HiShort(message);
if (strID == 0)
strID = STR_TCLfailMsgs; // use built-in messages
else
strID += kUserFailMsgBase; // use user’s message STR#
GetIndString(errStr, strID, strIndex);
}
// if still no message, check for 'Estr' resource
if (errStr[0] == 0) {
StringHandle strH;
strH = (StringHandle) GetResource(ErrMsg_Res, err);
if (!strH)
strH = GetString(STRosError2);
if (strH)
CopyPString(*strH, errStr);
}
// convert Mac error# and user’s host name to strings
NumToString(err, numStr);
NumToString(parm3, numStr3);
BlockMove(&hostCName, &hostStr, 255);
CtoPstr((char *) &hostStr);
if (hostStr[hostStr[0]] == '.')
hostStr[0]--;
ParamText(errStr, numStr, hostStr, numStr3);
// avoid infinite recursion in error handling by specifically
// testing if the ALRT and DITL resources we need are there
if ((GetResource('ALRT', alertID) == NULL) ||
(GetResource('DITL', alertID) == NULL))
{
if (gError)
gError->MissingResources();
else
ExitToShell(); // nothing else we can do...
}
// show the error alert
PositionDialog('ALRT', alertID);
InitCursor();
return (StopAlert(alertID, NULL));
}
// —— TCP notification routines ——
/*______________________________________________________________________
**
** ProviderChanged
**
** One of this object’s providers has just changed. Check to see if it is the TCP stream
** or resolver object. If so, process the notification.
**
** aProvider (CCollaborator *): the provider which changed
** reason (long): the reason code which was provided
** info (void *): additional info related to the reason
**
*/
void CTCPSessionDoc::ProviderChanged (CCollaborator *aProvider, long reason, void *info)
{
// find out who changed…
if (aProvider == itsStream) {
// it’s our stream, all right… find out what happened
switch (reason) {
case tcpStreamClosed:
HandleClosed();
break;
case tcpStreamClosing:
HandleClosing((Boolean) info);
break;
case tcpStreamDataArrived:
HandleDataArrived(((DataArrivedInfo *) info)->theData,
((DataArrivedInfo *) info)->theDataSize,
((DataArrivedInfo *) info)->isUrgent);
break;
case tcpStreamDataSent:
HandleDataSent((wdsEntry *) info);
break;
case tcpStreamICMP:
HandleICMP((struct ICMPReport *) info);
break;
case tcpStreamOpened:
HandleOpened();
break;
case tcpStreamOpenFailed:
HandleOpenFailed((OSErr) info);
break;
case tcpStreamSendFailed:
HandleSendFailed(((SendFailedInfo *) info)->WDSPtr,
((SendFailedInfo *) info)->theResultCode);
break;
case tcpStreamTCPError:
HandleTCPError(((TCPErrorInfo *) info)->theResultCode,
((TCPErrorInfo *) info)->theCsCode);
break;
case tcpStreamTerminated:
HandleTerminated(((TerminatedInfo *) info)->terminReason,
((TerminatedInfo *) info)->aboutToDispose);
break;
case tcpStreamTimeout:
HandleTimeout();
break;
case tcpStreamUnexpectedData:
HandleUnexpectedData();
break;
case tcpStreamUrgentBegin:
HandleUrgentBegin();
break;
}
}
else if (aProvider == itsResolver) {
// it’s the resolver… find out what happened
switch (reason) {
case tcpResolverStrToAddr:
HandleStrToAddr((struct hostInfo *) info);
break;
case tcpResolverAddrToName:
HandleAddrToName((struct hostInfo *) info);
break;
case tcpResolverHInfo:
HandleHInfo((struct returnRec *) info);
break;
case tcpResolverMXInfo:
HandleMXInfo((struct returnRec *) info);
break;
}
}
else // not our stream, let someone else handle it…
CDocument::ProviderChanged(aProvider, reason, info);
}
/*______________________________________________________________________
**
** HandleClosed (protected method)
**
** Respond to notification that the session has closed.
**
*/
void CTCPSessionDoc::HandleClosed (void)
{
// null method
}
/*______________________________________________________________________
**
** HandleClosing (protected method)
**
** Respond to notification that the session is being closed.
**
** remoteClosing (Boolean): TRUE if close initiated by remote host
**
*/
void CTCPSessionDoc::HandleClosing (Boolean remoteClosing)
{
b_16 cState;
AutoTitle();
if (remoteClosing) {
if (goAwayOnClose)
Close(FALSE);
else {
if (itsStream) { // make sure that we disconnect even if window remains
cState = itsStream->ConnectionState();
if (sessionOpen)
itsStream->Close();
if (sessionOpening)
itsStream->Abort();
}
}
}
}
/*______________________________________________________________________
**
** HandleDataArrived (protected method)
**
** Process notification that data has arrived.
**
** theData (Ptr): pointer to the data
** theDataSize (b_16): size of data buffer
** isUrgent (Boolean): TRUE if urgent mode is on
**
*/
void CTCPSessionDoc::HandleDataArrived (Ptr theData, b_16 theDataSize, Boolean isUrgent)
{
// null method
}
/*______________________________________________________________________
**
** HandleDataSent (protected method)
**
** Respond to notification that data has been successfully sent.
**
** WDSPtr (wdsEntry *): the WDS for the data which was sent
**
*/
void CTCPSessionDoc::HandleDataSent (wdsEntry *WDSPtr)
{
// null method
}
/*______________________________________________________________________
**
** HandleICMP (protected method)
**
** Respond to an ICMP message which was received.
**
** icmpMsg (struct ICMPReport *): the ICMP message report
**
*/
void CTCPSessionDoc::HandleICMP (struct ICMPReport *icmpMsg)
{
// null method
}
/*______________________________________________________________________
**
** HandleOpened (protected method)
**
** Respond to successful completion of a TCPPassiveOpen or TCPActiveOpen command.
**
*/
void CTCPSessionDoc::HandleOpened (void)
{
actualPort = itsStream->itsRemotePort;
AutoTitle();
}
/*______________________________________________________________________
**
** HandleOpenFailed (protected method)
**
** Respond to notification that a TCPOpen command failed.
**
** theResultCode (OSErr): the reason for failure
*/
void CTCPSessionDoc::HandleOpenFailed (OSErr theResultCode)
{
TCPErrorAlert(theResultCode, 0L, ALRT_TCPOpenFailed, 1);
Close(FALSE);
FailOSErr(kSilentErr);
}
/*______________________________________________________________________
**
** HandleSendFailed (protected method)
**
** Respond to failure to send data. This can be overriden to retry the send; however, you
** must be careful to duplicate the WDS since CTCPStream::HandleSendFailed disposes of
** the WDS and its contents after calling this routine.
**
** WDSPtr (wdsEntry *): the WDS structure for the data that wasn’t sent
** theResultCode (OSErr): the reason for failure
**
*/
void CTCPSessionDoc::HandleSendFailed (wdsEntry *WDSPtr, OSErr theResultCode)
{
TCPErrorAlert(theResultCode, 0L, ALRT_TCPSendFailed, 0);
}
/*______________________________________________________________________
**
** HandleTCPError (protected method)
**
** Respond to failure of a TCP command that was not expected. Default method displays a
** dialog, but takes no other action.
**
** theResultCode (OSErr): the result code
** theCsCode (short): the TCP command number that failed
**
*/
void CTCPSessionDoc::HandleTCPError (OSErr theResultCode, short theCsCode)
{
TCPErrorAlert(theResultCode, 0L, ALRT_TCPUnexpError, theCsCode);
}
/*______________________________________________________________________
**
** HandleTerminated (protected method)
**
** Respond to session termination event.
**
** terminReason (b_16): reason for termination (see TCP dev guide, p93)
** aboutToDispose (Boolean): TRUE if TCP stream will now dispose of itself
**
*/
void CTCPSessionDoc::HandleTerminated (b_16 terminReason, Boolean aboutToDispose)
{
// display error alert unless due to normal closure
if ((terminReason != 6) && (terminReason != 7))
TCPErrorAlert(terminReason + ESTR_TerminBase, 0L, ALRT_TCPTerminated, 0);
// retitle window & close if requested
AutoTitle();
if (goAwayOnClose)
Close(closeAndQuit);
// mark TCP stream as gone if appropriate
if (aboutToDispose)
itsStream = NULL;
}
/*______________________________________________________________________
**
** HandleTimeout (protected method)
**
** Respond to a ULP timeout event (remote TCP failed to respond in time). This method is
** only called if the ULP timeout action is set to abort & notify.
**
*/
void CTCPSessionDoc::HandleTimeout (void)
{
// null method
}
/*______________________________________________________________________
**
** HandleUnexpectedData (protected method)
**
** Respond to notification that data arrived without a receive command. If auto-receive is
** used, this method should never be called.
**
*/
void CTCPSessionDoc::HandleUnexpectedData (void)
{
// null method
}
/*______________________________________________________________________
**
** HandleUrgentBegin (protected method)
**
** Respond to notification that urgent data is arriving.
**
*/
void CTCPSessionDoc::HandleUrgentBegin (void)
{
// null method
}
/*______________________________________________________________________
**
** HandleStrToAddr (protected method)
**
** Respond to nofitication that a resolver StrToAddr call has completed. Most of this code
** is written to complete the OpenUserHost method. If OpenUserHost was issued, this method
** proceeds to establish a connection with the IP host which was named by the user.
**
** theHostInfo (struct hostInfo *): the hostInfo record (see MacTCP Dev Guide, p70)
**
*/
void CTCPSessionDoc::HandleStrToAddr (struct hostInfo *theHostInfo)
{
// if OpenUserHost was issued, finish the job
if ((pendingOpenByName) && (itsStream)) {
if ((*theHostInfo).rtnCode == noErr) {
// success, open the connection
hostAddress = (*theHostInfo).addr[0];
pendingOpenByName = FALSE;
itsStream->OpenConnection(FALSE, hostAddress,
pendingPortNumber, 0);
// if desired, get the canonical name
if (useCName) {
if ((*theHostInfo).cname[0] == 0) {
itsResolver->DoAddrToName(hostAddress);
pendingOpenByName = TRUE;
}
else
BlockMove(&(*theHostInfo).cname, &hostCName, 255);
}
// set the window title to reflect the name
AutoTitle();
} else {
// failed, issue alert & quit
pendingOpenByName = FALSE;
HandleOpenFailed((*theHostInfo).rtnCode);
} // if (...noErr)
} // if (pendingOpenByName)
}
/*______________________________________________________________________
**
** HandleAddrToName (protected method)
**
** Respond to nofitication that a resolver AddrToName call has completed.
**
** theHostInfo (struct hostInfo *): the hostInfo record (see MacTCP Dev Guide, p75)
**
*/
void CTCPSessionDoc::HandleAddrToName (struct hostInfo *theHostInfo)
{
// if OpenUserHost was issued, grab the hostname
if ((pendingOpenByName) && (useCName)) {
if ((*theHostInfo).rtnCode == noErr) {
pendingOpenByName = FALSE;
if ((*theHostInfo).cname[0])
BlockMove(&(*theHostInfo).cname, &hostCName, 255);
AutoTitle();
}
}
}
/*______________________________________________________________________
**
** HandleHInfo (protected method)
**
** Respond to nofitication that a resolver HInfo call has completed.
**
** theHInfo (struct returnRec *): the returnRec record (see MacTCP Dev Guide, p76)
**
*/
void CTCPSessionDoc::HandleHInfo (struct returnRec *theHInfo)
{
// null method
}
/*______________________________________________________________________
**
** HandleMXInfo (protected method)
**
** Respond to nofitication that a resolver MXInfo call has completed.
**
** theMXInfo (struct returnRec *): the returnRec record (see MacTCP Dev Guide, p77)
**
*/
void CTCPSessionDoc::HandleMXInfo (struct returnRec *theMXInfo)
{
// null method
}